//------------------------------------------------------------
//  Filename: camera_sync.v
//   
//  Author  : wlduan@gmail.com
//  Revise  : 2019-12-07 00:06
//  Description: 
//   
//  Copyright (C) 2014, YRBD, Inc.                           
//  All Rights Reserved.                                       
//-------------------------------------------------------------
//
`timescale 1ns/1ps
 
module CAMERA_SYNC #(
    parameter DEEPTH = 8,
    parameter WIDTH  = 9
) (
    input  wire                rst_n,
    
    input  wire                wr_clk,
    input  wire                wr_en,
    input  wire [WIDTH-1 : 0]  wr_din,
    
    input  wire                rd_clk,
    input  wire                rd_en,
    output wire [WIDTH-1 : 0]  rd_dout,
    
    output reg                 full,
    output reg                 empty,

    output reg                 overflw,
    output reg                 underflw
);
//--------------------------------------------------------
localparam PTR_BITS = 3;//$clog2(DEEPTH);
//--------------------------------------------------------
reg [WIDTH-1     : 0]  mem[DEEPTH-1 : 0];
wire[PTR_BITS-1  : 0]  waddr,raddr;
wire[PTR_BITS-1  : 0]  wbin_next,rbin_next,xbin_next;
wire[PTR_BITS-1  : 0]  wgray_next,rgray_next,xgray_next;
reg [PTR_BITS-1  : 0]  wbin,rbin,wp,rp;
reg [PTR_BITS-1  : 0]  wr1_rp,wr2_rp,rd1_wp,rd2_wp;
//--------------------------------------------------------
wire empty_val,full_val;
//--------------------------------------------------------
assign rd_dout = mem[raddr];
//--------------------------------------------------------
always@(posedge wr_clk) if(wr_en) mem[waddr] <= wr_din;
//--------------------------------------------------------
assign raddr      = rbin;
assign rbin_next  = rbin + rd_en;
assign rgray_next = rbin_next ^ (rbin_next >> 1);
//--------------------------------------------------------
//1.产生存储实体的读地址raddr; 
//2.将普通二进制转化为格雷码，并赋给读指针rp
always@(posedge rd_clk or negedge rst_n) begin
    if(!rst_n) begin
    	{rbin,rp} <= 0;
    end
    else begin
        {rbin,rp} <= {rbin_next,rgray_next};
    end
end
//--------------------------------------------------------
assign waddr      = wbin;
assign wbin_next  = wbin + wr_en;
assign wgray_next = wbin_next ^ (wbin_next >> 1);
assign xbin_next  = wbin + 1'b1 + wr_en;
assign xgray_next = xbin_next ^ (xbin_next >> 1);
//--------------------------------------------------------
//1.产生存储实体的写地址waddr; 
//2.将普通二进制转化为格雷码，并赋给写指针wp
always@(posedge wr_clk or negedge rst_n) begin
    if(!rst_n) begin
        {wbin,wp} <= 0;
    end
    else begin
        {wbin,wp} <= {wbin_next,wgray_next};
    end
end
//--------------------------------------------------------
//将读指针rp同步到写时钟域
always@(posedge wr_clk or negedge rst_n) begin
    if(!rst_n) begin
        {wr2_rp,wr1_rp} <= 0;
    end
    else begin
        {wr2_rp,wr1_rp} <= {wr1_rp,rp};
    end
end
//--------------------------------------------------------
//将写指针wp同步到读时钟域
always@(posedge rd_clk or negedge rst_n) begin
    if(!rst_n) begin
        {rd2_wp,rd1_wp} <= 0;
    end
    else begin
        {rd2_wp,rd1_wp} <= {rd1_wp,wp};
    end
end
//--------------------------------------------------------
//产生读空信号
assign empty_val = (rd2_wp == rgray_next);
//--------------------------------------------------------
always@(posedge rd_clk or negedge rst_n) begin
    if(!rst_n) begin
        empty <= 1'b1;
    end
    else begin
        empty <= empty_val;
    end
end
//--------------------------------------------------------
//产生写满信号
assign full_val = (wr2_rp == xgray_next);
//--------------------------------------------------------
always@(posedge wr_clk or negedge rst_n) begin
    if(!rst_n) begin
        full <= 1'b0;
    end
    else begin
        full <= full_val;
    end
end
//--------------------------------------------------------
always@(posedge wr_clk or negedge rst_n) begin
    if(!rst_n) begin
        overflw <= 1'b0;
    end
    else if(full&wr_en) begin
        overflw <= 1'b1;
    end
end

//--------------------------------------------------------
always@(posedge rd_clk or negedge rst_n) begin
    if(!rst_n) begin
        underflw <= 1'b0;
    end
    else if(empty&rd_en) begin
        underflw <= 1'b1;
    end
end

endmodule
